﻿using System;
using System.Collections;
using System.Linq;
using System.Text;
using LocalView;
using DataType;
using Google.GData.Client;
using Google.Contacts;
using Google.Documents;
using Google.GData.Documents;
using System.IO;
using Google.GData.AccessControl;
using Excel = Microsoft.Office.Interop.Excel;
using Utilities;

namespace MyGDocsAddIn
{
    public class ExcelLocalView : ILocalView
    {
        public delegate void StateNotify(string key);

        private global::System.Object missing = global::System.Type.Missing;

        public Excel.Application m_App;

        public override event EventHandler<Credentials> Login;
        //public event EventHandler Logout;
        public override event EventHandler<FeedWrapper<Contact>> GetContacts;
        public override event EventHandler<FeedWrapper<Document>> GetDocuments;
        public override event EventHandler<FeedWrapper<Document>> GetFolders;
        public override event EventHandler<DocumentInfo> UploadDocument;
        public override event EventHandler<DocumentInfo> UpdateDocument;
        public override event EventHandler<DownloadInfo> DownloadDocument;
        public override event EventHandler<ShareInfoCollection> ShareDocument;
        public override event EventHandler<ShareInfoCollection> GetDocumentAcl;
        public override event EventHandler<DocumentInfo> LookForDocument;
        public override event EventHandler<DocumentInfo> OpenDocumentInCloud;

        private Excel.AppEvents_Event m_excelEvents;
        private DbOperations m_dbOperations;
        public StateNotify m_LoadState;
        public StateNotify m_StoreState;

        public ExcelLocalView(Excel.Application App, DbOperations dbOperations)
        {
            m_App = App;
            m_DocInfo = null;
            m_dbOperations = dbOperations;
        }

        public override string GetDocumentPath()
        {
            return m_App.ActiveWorkbook.FullName;
        }

        public override string GetDocumentName()
        {
            return m_App.ActiveWorkbook.Name;
        }

        public override void DoLogin(Credentials cred)
        {
            if (Login != null)
                Login(this, cred);
        }

        //public override void DoLogOut()
        //{
        //    if (Logout != null)
        //        Logout(this, new EventArgs());
        //}

        public override void DoDownloadDocument(DownloadInfo info)
        {
            if (DownloadDocument != null)
                DownloadDocument(this, info);
        }

        public override void DoGetContacts(FeedWrapper<Contact> feed)
        {
            if (GetContacts != null)
                GetContacts(this, feed);
        }

        public void DoGetContactsAsync(NotifyDelegate<Contact> Notify)
        {
            if (GetContacts != null)
            {
                DoIterationASync<Contact> Iteration = new DoIterationASync<Contact>(this.GetContacts, Notify, this.dummy);
                Iteration.StartAction();
            }
        }

        public override void DoGetDocuments(FeedWrapper<Document> feed)
        {
            if (GetDocuments != null)
                GetDocuments(this, feed);
        }

        public void DoGetDocumentsAsync(NotifyDelegate<Document> Notify)
        {
            if (GetDocuments != null)
            {
                DoIterationASync<Document> Iteration = new DoIterationASync<Document>(this.GetDocuments, Notify, this.dummy);
                Iteration.StartAction();
            }
        }

        private void ShareWorkbook()
        {
            try{
                m_App.DisplayAlerts = false;

                m_App.ActiveWorkbook.Save();

                string fname = m_App.ActiveWorkbook.FullName;
                m_App.ActiveWorkbook.SaveAs(m_App.ActiveWorkbook.FullName,
                                            m_App.ActiveWorkbook.FileFormat,
                                            missing, //password
                                            missing, //writeRefPassword
                                            missing, //readOnlyRecommended
                                            missing, //createBackup
                                            Excel.XlSaveAsAccessMode.xlShared,
                                            Excel.XlSaveConflictResolution.xlUserResolution,
                                            missing,
                                            missing,
                                            missing,
                                            missing);
            }
            finally{
                m_App.DisplayAlerts = true;
            }
        }

        public override void DoSaveDocumentInCloud()
        {
            ShareWorkbook();
            if (UploadDocument != null)
            {
                m_DocInfo = new DocumentInfo(m_App.ActiveWorkbook.FullName, m_App.ActiveWorkbook.Name);
                UploadDocument(this, m_DocInfo);
                m_dbOperations.DoRegisterLocalCopy(m_DocInfo);
            }
        }


        public override void MergeContent(string fileName, Stream source)
        {
            ShareWorkbook();

            string tmpPath = m_App.ActiveWorkbook.Path + "\\" + m_App.ActiveWorkbook.Name + "_tmp.xls";

            FileInfo fileInfo = new FileInfo(tmpPath);
            FileStream stream = fileInfo.Open(FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
            CommonOperations.DoSaveDocumentContent(stream, source);

            MergeDocumentWith(tmpPath);
            
            highLightWorkbook();
        }

        private void highLightWorkbook()
        {
            m_App.ActiveWorkbook.HighlightChangesOptions(Excel.XlHighlightChangesTime.xlAllChanges,
                                                         "Everyone",
                                                         missing);

            m_App.ActiveWorkbook.HighlightChangesOnScreen = true;
            m_App.ActiveWorkbook.Save();
        }

        private void MergeDocumentWith(string serverCopyPath)
        {
            Excel.Workbook wb = m_App.ActiveWorkbook;
            string suffix = "_copy.xls";
            string copy_path = wb.FullName;
            
            // never save copy before, so save it at first time
            if (!copy_path.EndsWith(suffix, System.StringComparison.CurrentCultureIgnoreCase))
            {
                copy_path = copy_path.Replace(".xls", "_copy.xls");
                wb.SaveCopyAs(copy_path);
            }

            Excel.Workbook localCopy = m_App.Workbooks.Open(copy_path,
                                   missing, missing, missing, missing, missing, missing, missing,
                                   missing, missing, missing, missing, missing, missing, missing);
            m_App.ActiveWindow.Visible = false;

            Excel.Workbook serverCopy = m_App.Workbooks.Open(serverCopyPath,
                                   missing, missing, missing, missing, missing, missing, missing,
                                   missing, missing, missing, missing, missing, missing, missing);
            m_App.ActiveWindow.Visible = false;

            Int32 lcSheetsCount = localCopy.Sheets.Count;
            Int32 rsSheetsCount = serverCopy.Sheets.Count;
            for (Int32 sh = 1; sh <= rsSheetsCount; ++sh)
            {
                Excel.Worksheet serverWorksheet = serverCopy.Sheets[sh] as Excel.Worksheet;
                Excel.Worksheet localWorksheet = null;
                if (sh <= lcSheetsCount)
                    localWorksheet = localCopy.Sheets[sh] as Excel.Worksheet;
                else
                    localWorksheet = localCopy.Sheets.Add(missing, localCopy.Sheets[sh - 1], missing, missing) as Excel.Worksheet;

                //it take hours to iterate through all cells
                Int32 rowCount = localWorksheet.Cells.Rows.Count;
                Int32 columnCount = localWorksheet.Cells.Columns.Count;

                //current limitation in order to avoid performance issue
                for (Int32 i = 1; i <= 20; ++i)
                    for (Int32 j = 1; j <= 40; ++j)
                        if ((((Excel.Range)serverWorksheet.Cells[i, j]).Value2 != null) &&
                            (((Excel.Range)serverWorksheet.Cells[i, j]).Value2.ToString().Length != 0))
                            localWorksheet.Cells[i, j] = serverWorksheet.Cells[i, j];
            }

            localCopy.Close(true, missing, false);
            serverCopy.Close(true, missing, false);

            //merge shared copy with original
            wb.Save();
            wb.MergeWorkbook(copy_path);
            wb.Save();
        }

        public override void DoLookupRemoteCopy(DocumentInfo info)
        {
            //at first look for document id in db, if no document then exception happens
            m_dbOperations.DoLookupRemoteCopy(info);

            if (LookForDocument != null)
                LookForDocument(this, info);
        }

        public override void DoUpdateDocument()
        {
            try{
                if (UpdateDocument != null)
                {
                    m_App.ActiveWorkbook.Save();
                    UpdateDocument(this, m_DocInfo);
                }
            }
             catch(GDataRequestException e)
            {
                Utilities.TraceWrapper.LogException("DoUpdateDocument error: " + e.Message, e);
                 
                //create later lookup for type depending extension using DocumentEntry.IsSpreadsheet and so on
                Document.DownloadType type = Document.DownloadType.xls;
                Document dt = new Document();
                dt.AtomEntry = m_DocInfo.m_DocEntry;
                DownloadInfo info = new DownloadInfo(dt, type);
                DownloadDocument(this, info);
                
                //get new DocumentEnty
                DoLookupRemoteCopy(m_DocInfo);

                Excel.Workbook wb = m_App.ActiveWorkbook;
                string tmpPath = wb.Path + "\\" + wb.Name + "_tmp.xls";

                FileInfo fileInfo = new FileInfo(tmpPath);
                FileStream stream = fileInfo.Open(FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
                CommonOperations.DoSaveDocumentContent(stream, info.m_Stream);

                MergeDocumentWith(tmpPath);
                
                //try to update document again
                DoUpdateDocument();
            }

            highLightWorkbook();
        }

        private void BuildListOfUsers(IEnumerable users, Permission perm, AclFeed feed)
        {
            string permStr = null;
            switch (perm)
            {
                case Permission.reader: permStr = "reader"; break;
                case Permission.writer: permStr = "writer"; break;
            }

            foreach (object obj in users)
            {
                AclEntry entry = new AclEntry();
                entry.Scope = new AclScope();
                entry.Scope.Type = AclScope.SCOPE_USER;
                entry.Scope.Value = obj.ToString();
                entry.Role = new AclRole(permStr);

                feed.Entries.Add(entry);
            }
        }

        private void dummy()
        {}

        private void RefreshEtag()
        {
            DoLookupRemoteCopy(m_DocInfo);
        }

        public override void UpdateDocumentInfo()
        {
            m_DocInfo = new DocumentInfo(m_App.ActiveWorkbook.FullName, m_App.ActiveWorkbook.Name);
            DoLookupRemoteCopy(m_DocInfo);
        }

        public override void DoShareDocumentsAsync(IEnumerable users, Permission perm)
        {
            if (ShareDocument != null)
            {
                ShareInfoCollection info = new ShareInfoCollection(m_DocInfo.m_DocEntry);
                BuildListOfUsers(users, perm, info.m_Feed);

                DoOperationASync<ShareInfoCollection> operation = new DoOperationASync<ShareInfoCollection>(this.ShareDocument, info, this.RefreshEtag);
                operation.StartAction();
            }
        }

        public override void DoOpenLocalCopyShared(string filePath)
        {
            try
            {
                Excel.Workbook wb = m_App.Workbooks.Open(filePath, false, false, missing, missing, missing, true, missing, missing,
                                    missing, missing, missing, missing, missing, missing);
                ShareWorkbook();
            }
            catch (Exception e)
            {
                Utilities.TraceWrapper.LogException("DoOpenLocalCopyShared error: " + e.Message, e);
            }
        }

        public override void DoOpenLocalCopyNotShared(string filePath)
        {
            try
            {
                Excel.Workbook wb = m_App.Workbooks.Open(filePath, false, false, missing, missing, missing, true, missing, missing,
                                    missing, missing, missing, missing, missing, missing);
                //wb.Save();
                //ShareWorkbook(m_App.ActiveWorkbook.FullName, m_App.ActiveWorkbook.FileFormat);
            }
            catch (Exception e)
            {
                Utilities.TraceWrapper.LogException("DoOpenLocalCopyNotShared error: " + e.Message, e);
            }
        }


        public override string GetInviteUri()
        { return m_DocInfo.m_DocEntry.AlternateUri.Content; }


        public override void DoOpenDocumentInCloud(DocumentInfo info)
        {
            if (OpenDocumentInCloud != null)
                OpenDocumentInCloud(this, info);
        }

        public override void DoGetDocumentAcl(ShareInfoCollection info)
        {
            if (GetDocumentAcl != null)
                GetDocumentAcl(this, info);
        }

        public override bool IsDocumentOpened(string path)
        {
            foreach (Excel.Workbook wb in m_App.Workbooks)
                if (wb.FullName == path)
                    return true;

            return false;
        }

        public override void CloseDocument(string path)
        {
            foreach (Excel.Workbook wb in m_App.Workbooks)
                if (wb.FullName == path)
                    wb.Close(false, null, false);
        }

        public override void ActivateDocument(string path)
        {
            foreach (Excel.Workbook wb in m_App.Workbooks)
                if (wb.FullName == path)
                    wb.Activate();
        }
    }
}
